/// Based on the output you've provided, it seems like you're working with a Rust implementation of the Stochastic Oscillator from the YATA library,
/// specifically using custom parameters for the moving averages.
/// The output format indicates the status of three different signals (S) and two main values (V) for each iteration of your loop,
/// as the Stochastic Oscillator processes the input candlestick data.
///
/// Understanding the Output
/// S: [N, N, N] or S: [N, N, +1.00] / S: [N, N, -1.00]: This part of the output represents the three signals generated by the Stochastic Oscillator:
///
/// Signal #1 and #2: The first two 'N' denote that neither Signal #1 nor Signal #2 has been triggered. 'N' stands for no signal,
/// meaning the main value or signal line value did not cross their respective bounds.
/// Signal #3: The third position in the array denotes Signal #3, which relates to the crossover of the main value and the signal line.
/// '+1.00' indicates a full buy signal when the main value crosses the signal line upwards,
/// while '-1.00' signals a full sell when the main value crosses the signal line downwards.
/// V: [x, y]: These are the two main values produced by the Stochastic Oscillator:
///
/// The first value (x) is the main Stochastic Oscillator value, normalized to a range between 0.0 and 1.0.
/// It reflects the position of the current close relative to the high-low range over the specified period.
/// The second value (y) is the signal line value, also normalized, which is a smoothed version of the main stochastic value using the specified moving average (signal field).
/// Observations from Your Output
/// Most of the iterations show 'S: [N, N, N]', indicating that during those periods, neither the main value crossing its bounds (Signals #1 and #2)
/// nor the main value crossing the signal line (Signal #3) triggered a buy or sell signal.
/// At idr[494] and idr[502], you have S: [N, N, +1.00] and S: [N, N, -1.00] respectively,
/// signifying a full buy signal at idr[494] when the main value crossed above the signal line
/// and a full sell signal at idr[502] when the main value crossed below the signal line.
/// Conclusion
/// Your Stochastic Oscillator implementation is functioning as expected, providing insights into potential buy/sell signals
/// based on the crossovers and boundaries defined by the oscillator's calculations.
/// The lack of Signals #1 and #2 activations suggests the main value and the signal line value have not crossed their respective upper or lower bounds
/// during most of the observed periods, while the occasional Signal #3 activations highlight points where the main stochastic line crossed the signal line,
/// indicating potential trading opportunities.
///
/// idr[489] close=183.009 ==> S: [N, N, N], V: [ 0.3914,  0.4094]
/// idr[490] close=183.01 ==> S: [N, N, N], V: [ 0.3594,  0.3870]
/// idr[491] close=183.356 ==> S: [N, N, N], V: [ 0.3446,  0.3651]
/// idr[492] close=183.5 ==> S: [N, N, N], V: [ 0.3400,  0.3480]
/// idr[493] close=183.459 ==> S: [N, N, N], V: [ 0.3194,  0.3347]
/// idr[494] close=183.3 ==> S: [N, N, +1.00], V: [ 0.3319,  0.3304]
/// idr[495] close=183.424 ==> S: [N, N, N], V: [ 0.3416,  0.3310]
/// idr[496] close=183.207 ==> S: [N, N, N], V: [ 0.3423,  0.3386]
/// idr[497] close=183.366 ==> S: [N, N, N], V: [ 0.3582,  0.3474]
/// idr[498] close=183.33 ==> S: [N, N, N], V: [ 0.3719,  0.3575]
/// idr[499] close=183.138 ==> S: [N, N, N], V: [ 0.3720,  0.3674]
/// idr[500] close=183.001 ==> S: [N, N, -1.00], V: [ 0.3625,  0.3688]
/// idr[501] close=183.194 ==> S: [N, N, N], V: [ 0.3640,  0.3662]
/// idr[502] close=183.189 ==> S: [N, N, +1.00], V: [ 0.3650,  0.3638]
/// idr[503] close=183.173 ==> S: [N, N, N], V: [ 0.3679,  0.3656]
/// idr[504] close=183.278 ==> S: [N, N, N], V: [ 0.3783,  0.3704]
/// idr[505] close=183.58 ==> S: [N, N, N], V: [ 0.3936,  0.3799]
///
use crate::db::FUDB;
use crate::model::indicators;
use crate::model::indicators::kdj;
use crate::model::indicators::IndicatorResultWrap;
///
use crate::model::kline::Kline;
use crate::model::FudaRecord;
use anyhow::anyhow;
use anyhow::ensure;
use anyhow::Result;
use surrealdb::opt::PatchOp;
use surrealdb::sql::Id;
use surrealdb::sql::Thing;
use yata::core::Candle;
use yata::core::IndicatorConfig;
use yata::core::IndicatorInstance;
use yata::indicators::StochasticOscillatorInstance;

#[derive(Debug)]
pub struct Kdj {
    kdj_instance: StochasticOscillatorInstance,
}

impl Kdj {
    pub fn new(conf: &kdj::KDJConfig, init_kline: &Kline) -> Result<Self> {
        let kdj = yata::indicators::StochasticOscillator {
            period: conf.period,
            ma: yata::helpers::MA::SMA(conf.ma),
            signal: yata::helpers::MA::SMA(conf.signal),
            zone: conf.zone,
        };
        let candle: Candle = init_kline.into();
        let kdj_instance: StochasticOscillatorInstance = kdj.init(&candle)?;
        Ok(Kdj { kdj_instance })
    }

    pub fn next(&mut self, kline: &Kline) -> kdj::KDJ {
        Self::compute(&mut self.kdj_instance, kline)
    }

    pub fn repeat(&mut self, kline: &Kline) -> kdj::KDJ {
        let mut kdj_instance = self.kdj_instance.clone();
        Self::compute(&mut kdj_instance, kline)
    }

    fn compute(kdj_instance: &mut StochasticOscillatorInstance, kline: &Kline) -> kdj::KDJ {
        let candle: Candle = kline.into();

        let idr: yata::core::IndicatorResult = kdj_instance.next(&candle);

        let idrw: IndicatorResultWrap = IndicatorResultWrap::from(&idr);

        let value = kdj::KDJ {
            signal0: idrw.s0,
            signal1: idrw.s1,
            signal2: idrw.s2,
            main: idrw.v0,
            signal: idrw.v1,
        };

        return value;
    }
}

/// 更新指定K线数据的KDJ,现在只能做全量计算
///
/// @param klines: K线数据
///
/// config: Kdj 设置
///
/// return 更新的记录数
///
pub async fn update_kdj(
    klines: &Vec<Kline>,
    config: Option<indicators::kdj::KDJConfig>
) -> Result<u32> {
    ensure!(klines.len() > 0, "klines is empty");
    let mut updates = 0u32;
    if let Some(fstk) = klines.first() {
        let kdj = match config {
            Some(conf) =>
                yata::indicators::StochasticOscillator {
                    period: conf.period,
                    ma: yata::helpers::MA::SMA(conf.ma),
                    signal: yata::helpers::MA::SMA(conf.signal),
                    zone: conf.zone,
                },
            None => yata::indicators::StochasticOscillator::default(),
        };

        let mut kdj: yata::indicators::StochasticOscillatorInstance = kdj.init(
            &(fstk.open, fstk.high, fstk.low, fstk.close, fstk.volume as f64)
        )?;

        for (i, kline) in klines.iter().enumerate() {
            if let Some(Thing { id: Id::String(kid_str), .. }) = &kline.id {
                let idr: yata::core::IndicatorResult = kdj.next(
                    &(kline.open, kline.high, kline.low, kline.close, kline.volume as f64)
                );

                println!("idr[{i}] close={} ==> {:?}", kline.close, idr);

                let idrw: IndicatorResultWrap = IndicatorResultWrap::from(&idr);

                let kdj = indicators::kdj::KDJ {
                    signal0: idrw.s0,
                    signal1: idrw.s1,
                    signal2: idrw.s2,
                    main: idrw.v0,
                    signal: idrw.v1,
                };

                let updated: Option<FudaRecord> = FUDB.update(("kline", kid_str))
                    .patch(PatchOp::replace("/kdj", kdj)).await
                    .map_err(|err| anyhow!(err))?;

                if let Some(_) = updated {
                    updates += 1;
                }
            }
        }
    }

    Ok(updates)
}

// [00], close:158.07=>kdj:Some(KDJ { idrw: IndicatorResultWrap { s0: None, v0: 0.09812550159682587, s1: None, v1: 0.519554529034741, s2: None, v2: 0.0, s3: None, v3: 0.0, slen: 2, vlen: 2 }, bar: 0.09812550159682587 })
// [01], close:157.97=>kdj:Some(KDJ { idrw: IndicatorResultWrap { s0: None, v0: 0.1445212466661019, s1: None, v1: 0.6249117858942198, s2: None, v2: 0.0, s3: None, v3: 0.0, slen: 2, vlen: 2 }, bar: 0.1445212466661019 })
// [02], close:157.13=>kdj:Some(KDJ { idrw: IndicatorResultWrap { s0: None, v0: 0.21181877951207184, s1: None, v1: 0.7450094207012493, s2: None, v2: 0.0, s3: None, v3: 0.0, slen: 2, vlen: 2 }, bar: 0.21181877951207184 })
// [03], close:158.71=>kdj:Some(KDJ { idrw: IndicatorResultWrap { s0: None, v0: 0.379855721006237, s1: None, v1: 0.8783070809985437, s2: None, v2: 0.0, s3: None, v3: 0.0, slen: 2, vlen: 2 }, bar: 0.379855721006237 })

#[cfg(test)]
mod test {
    use crate::db;
    use crate::model::indicators;
    use crate::model::kline::Klt;
    use crate::model::kline::KLINE_COMMON_FIELDS;

    #[tokio::test]
    async fn test_update_kdj() {
        let _ = db::init().await.unwrap();

        let klt_vec = vec![
            Klt::M005,
            Klt::M015,
            Klt::M030,
            Klt::M060,
            Klt::DAY,
            Klt::WEEK,
            Klt::MONTH
        ];
        for klt in &klt_vec {
            let klines = db::kline
                ::select("SZ.KZZ.128136", None, Some(klt), Some("klts ASC"), 0, 10000).await
                .unwrap();
            let result = db::indicators::kdj::update_kdj(&klines, None).await;
            println!("{:?}", result);
            assert_eq!(result.is_ok(), true);
        }

        let result = db::kline::select(
            "SZ.KZZ.128136",
            None,
            Some(&Klt::M005),
            Some("klts ASC"),
            0,
            100
        ).await;
        if let Ok(klines) = &result {
            for (index, kline) in klines.iter().enumerate() {
                if index > 30 {
                    break;
                }
                // println!(
                //     "[{:02}], close:{:.2}=>kdj:{:?}",
                //     index, kline.close, kline.kdj,
                // );
            }
        }
    }
}
// [00], close:158.07=>kdj:Some(KDJ { signal0: None, signal1: None, signal2: None, main_v: 0.3083, signal_v: 0.3228 })
// [01], close:157.97=>kdj:Some(KDJ { signal0: None, signal1: None, signal2: None, main_v: 0.3272, signal_v: 0.3482 })
// [02], close:157.13=>kdj:Some(KDJ { signal0: None, signal1: None, signal2: None, main_v: 0.3328, signal_v: 0.3769 })
// [03], close:158.71=>kdj:Some(KDJ { signal0: None, signal1: None, signal2: None, main_v: 0.3846, signal_v: 0.4222 })
// [04], close:156.67=>kdj:Some(KDJ { signal0: None, signal1: None, signal2: None, main_v: 0.4132, signal_v: 0.4654 })
// [05], close:159.00=>kdj:Some(KDJ { signal0: None, signal1: None, signal2: None, main_v: 0.4688, signal_v: 0.5145 })
// [06], close:158.79=>kdj:Some(KDJ { signal0: None, signal1: None, signal2: None, main_v: 0.5143, signal_v: 0.5618 })
// [07], close:158.32=>kdj:Some(KDJ { signal0: None, signal1: None, signal2: None, main_v: 0.5603, signal_v: 0.612 })
// [08], close:159.26=>kdj:Some(KDJ { signal0: None, signal1: None, signal2: None, main_v: 0.6108, signal_v: 0.6604 })
// [09], close:159.67=>kdj:Some(KDJ { signal0: None, signal1: None, signal2: None, main_v: 0.6648, signal_v: 0.6962 })
// [10], close:159.80=>kdj:Some(KDJ { signal0: None, signal1: None, signal2: None, main_v: 0.7057, signal_v: 0.7229 })
// [11], close:159.32=>kdj:Some(KDJ { signal0: None, signal1: None, signal2: None, main_v: 0.7179, signal_v: 0.7423 })
// [12], close:160.20=>kdj:Some(KDJ { signal0: None, signal1: None, signal2: None, main_v: 0.745, signal_v: 0.7648 })
